/**
 * 数据元件setter工厂
 * code by chunyuan.zhang
 * 
 * **/
import { useState, useEffect, type FC } from 'react';
import { useForm, observer } from '@formily/react';
import { ButtonGroups } from './ButtonGroups';
import { DataTypeSelect } from './DataTypeSelect';
import { GetFilds } from './GetFilds';
import { ValueTypeSelect, type IValueSource } from './ValueTypeSelect';
import { Logic, getLogic, type logickey } from './Logic';
import { Operator, getOperator, type operatorkey } from './Operator';
import { Sort } from './Sort';
import { Scope } from './Scope';
import { Eque } from './Eque';
import { getModelInfo } from './servers';
export type { IFornItem } from './interface'
import './style/index.less'

type IMetaDataFactoryKey = 'FC' | 'Sort' | 'ButtonGroups' | 'DataTypeSelect' | 'Eque' | 'GetFilds' | 'Logic' | 'Operator' | 'Scope' | 'ValueTypeSelect';

export interface IModelType {
  Describe: string | null
  ModelKey: string
  ModelType: number
  ModuleId: string
  Name: string
}

export interface IMetaDataOption {
  type: IMetaDataFactoryKey,
  props?: any;
  plugin?: FC<any>
};

interface IMetaDataFactory {
  cls: string;
  onChange: (val: any, i: number) => void;
  value: any;
  formValueLength?: number;//表单数据的长度
  addItem?: () => void;
  delItem?: (index: number) => void;
  metaDataWidgets: IMetaDataOption[];
  isDesigner?: boolean //是否后端逻辑编排
}


/** 模型字段获取hooks;
 * **/
export const useGetModalInfo = () => {
  const fromilyForm = useForm();
  const [data, $data] = useState([]);
  const appInfo = fromilyForm?.appInfo;
  const modalValue: IModelType = fromilyForm.getValuesIn('config.modal')
  useEffect(() => {
    if (modalValue?.ModelKey) {
      getModelInfo(appInfo.Id, [modalValue?.ModelKey]).then((res) => {
        $data(res);
      })
    }
  }, [appInfo?.Id, modalValue?.ModelKey]);

  return data
}


/**元件工厂组件;
 * **/
export const MetaDataFactory = (props: IMetaDataFactory) => {

  // 公共方法属性
  let widgetProps = {
    cls: props.cls,
    onChange: props.onChange,
    isDesigner: props.isDesigner
  }

  // 按钮组所需的props
  const buttonGroupsProps = {
    ...widgetProps,
    formValueLength: props.formValueLength,
    addItem: props.addItem,
    delItem: props.delItem
  }

  // 值组件所需的props
  const valueTypeSelectProps = {
    ...widgetProps,
    formValue: props.value
  }

  const setWidget = (type: IMetaDataFactoryKey, transferProps: any, plugin?: FC) => {

    let widget = null;
    // 合并属性
    const mereProps = {
      ...widgetProps,
      ...transferProps,
      value: props.value[transferProps.datakey]
    }

    switch (type) {
      case 'Scope': widget = <Scope {...widgetProps} {...mereProps} />; break;
      case 'DataTypeSelect': widget = <DataTypeSelect {...widgetProps}  {...mereProps} />; break;
      case 'Eque': widget = <Eque {...widgetProps}  {...props} />; break;
      case 'GetFilds': widget = <GetFilds {...widgetProps} {...mereProps} />; break;
      case 'Logic': widget = <Logic {...widgetProps}  {...mereProps} />; break;
      case 'Sort': widget = <Sort {...widgetProps}  {...mereProps} />; break;
      case 'Operator': widget = <Operator {...widgetProps} {...mereProps} />; break;
      case 'ValueTypeSelect': widget = <ValueTypeSelect {...valueTypeSelectProps} {...mereProps} />; break;
      case 'ButtonGroups': widget = <ButtonGroups {...buttonGroupsProps} {...mereProps} />; break;
      case 'FC': widget = plugin?.(mereProps); break;
      default: break;
    }
    return widget;
  }

  if (props.metaDataWidgets.length < 1) {
    console.warn('metaDataWidgets参数没有值,元件工厂无法启动')
    return <div></div>
  }
  return (
    <div className={`inbiz-metaDataFactory  ${props.cls}`}>
      {
        props.metaDataWidgets?.map((item) => {
          return <div className='inbiz-metaDataFactory-item'>{
            setWidget(item.type, item.props, item.plugin)
          }</div>
        })
      }
    </div>
  )
}

interface ISearchField {
  field: {
    type: string,
    name: string,
    key: string
  };
  format: any
}

//Graphql关键字
type IGraphqlKey = 'query' | 'add' | 'del' | 'update';
type IModel = string;
export type IFields = ISearchField[];


interface IAddField {
  field: {
    type: string,
    name: string,
    key: string
  };
  value: any
}

type IAddFieldArray = IAddField[]
interface IAddFields {
  type: 'one' | 'more',
  data: IAddFieldArray | IAddFieldArray[]
}

type IUpdateFields = IAddFieldArray;

const graphql = {
  'query': 'query',
  'add': 'mutation',
  'del': 'mutation',
  'update': 'mutation',
}

interface IGraphql {
  type: IGraphqlKey;
  model: IModel;
  fields?: IFields | IAddFields | IUpdateFields;
  where?: IWhere[];
  index?: number;
  size?: number;
  order?: IFields;
  delMethod?: boolean;
  editFields?: string;
}

const graphQLFormatMap = [
  'docFormat',
  'menberFormat',
  'modelFormat',
  'dateFormat',
  'dictionaryFormat',
  'jsonFormat'
]

const menberType = [
  'USER',
  '',
  'DEPT',
  'POSITION',
  "GROUP",
  'ALL'
];

const formatType = [
  'NAME',
  'NAME_AND_ACCOUNT'
]

const graphParamMap = [
  (format: any) => {
    return `type:${format.FormatTypeDetail}`
  },
  (format: any) => {
    const type = menberType[format.FormatTypeDetail] ?? 'ALL'
    const formatValue = formatType[format.MemberFormatType] ?? ''
    return `type:${type}, formatType:${formatValue}`
  },
  (format: any) => {
    return `showField: ${format.ShowField}, relationModel: ${format.RelationModel},relationField:${format.RelationField}`
  },
  (format: any) => {
    return `arg:${format.FormatTypeDetail}`
  },
  (format: any) => {
    return `arg:${format.FormatTypeDetail}`
  },
  (format: any) => {
    return `arg:${format.FormatTypeDetail}`
  },
]

export interface IWhere {
  field: {
    type: string,
    name: string,
  };
  logic: logickey
  operator: operatorkey
  scope1: string;
  scope2: string
  type: IValueSource
  value: string
}


interface IGroup {
  index: number,
  data: IWhere[],
  type: 'and' | 'or'
}

interface IGroupWhere {
  groups: IGroup[];
  others: IWhere[];
}


/**根据范围调整进行分组
 * **/
const getGroupWhere = (wheres: IWhere[]) => {
  // 范围数据与正常数据分组
  let groupWhere: IGroupWhere = {
    groups: [],
    others: []
  };
  // 根据范围分组
  let group: IGroup = {
    index: -1,
    data: [],
    type: 'and'
  };
  wheres.forEach((item, i: number) => {
    if (item.scope1) {
      group.index++
      group.data.push(item);
    } else if (item.scope2) {
      group.data.push(item);
      group.type = item.logic;
      groupWhere.groups.push(group);
    } else {
      groupWhere.others.push(item);
    }
  });
  return groupWhere
}

/** 聚合分组
 * **/
const polyWhere = (groupWhere: IGroupWhere) => {
  // 逻辑再聚合分组
  const typeGroups: {
    and: any;
    or: any
  } = {
    and: [],
    or: []
  };
  groupWhere.groups.forEach((item) => {
    const groupsData = getGraphQLWhereObj(item.data);
    if (item.type == 'and') {
      typeGroups.and.push(groupsData);
    } else {
      typeGroups.or.push(groupsData);
    }
  })
  return typeGroups
}


/** 查询条件组件,通用对象反扁平方法
 * **/
const getGraphQLWhereObj = (wheres: IWhere[]) => {
  let where: any = {};
  let field = null;
  let nextField = null;
  let temp = where;
  for (var i = 0; i < wheres.length; i++) {
    field = wheres[i];
    nextField = wheres[i + 1];
    if (nextField && nextField.field) {
      let nextObj = {};
      if (field?.field?.name) {
        temp[field.field.name] = {
          [getOperator(field.operator)]: field.value
        };
        temp[getLogic(field.logic)] = [nextObj];
        temp = nextObj;
      }
    } else {
      if (field?.field?.name) {
        temp[field.field.name] = {
          [getOperator(field.operator)]: field.value
        };
      }
    }
  }
  return where
}


/** 获取GraphQL筛选条件
 * **/
export const getGraphQLWhere = (wheres: IWhere[]) => {
  let where: any = {};
  // 范围数据与正常数据分组
  let groupWhere: IGroupWhere = getGroupWhere(wheres)

  if (groupWhere.groups.length > 0) {

    let typeGroups = polyWhere(groupWhere)
    let other = getGraphQLWhereObj(groupWhere.others);
    where = {
      [getLogic('or')]: typeGroups.or,
      [getLogic('and')]: typeGroups.and,
      ...other
    }

  } else {
    where = getGraphQLWhereObj(wheres);
  }
  return where
}



const getAddFieldString = (addFieldsData: IAddFieldArray) => {
  const fieldSNametrings = addFieldsData?.map((item) => {
    const formatTypeValue = (item.field.type == 'int' || item.field.type == 'boolean') ? item.value : `"${item.value}"`
    return `${item.field.name}:${formatTypeValue}`;
  });
  let addFieldString = fieldSNametrings?.join(',');
  return addFieldString
}

/**获取GraphQL组装好的字符串
 * **/
export const getGraphQL = (props: IGraphql) => {
  const { type, model, fields, where, index, size, delMethod, order } = props;

  let query = '';
  let variables = {};
  switch (type) {
    case 'add':
      const addFields = fields as IAddFields;
      // 新增一个
      if (addFields.type == 'one') {
        const addFieldsData = addFields.data as IAddFieldArray;
        let addFieldString = getAddFieldString(addFieldsData);

        const returnFieldStrings = addFieldsData?.map((item) => {
          return `${item.field.name}`;
        });

        let returnFieldString = returnFieldStrings.join(',');
        query = `${graphql[type]}{${type}_${model}_one(object:{${addFieldString}}){${returnFieldString}}}`;

        // 新增多个
      } else if (addFields.type == 'more') {

        const addFieldsArrays = addFields.data as IAddFieldArray[];
        const addFieldsArrayStrings = addFieldsArrays.map((addFieldsArray) => {
          const fieldSNametrings = getAddFieldString(addFieldsArray)
          return `{${fieldSNametrings}}`
        })

        let addFieldString = addFieldsArrayStrings?.join(',');
        query = `${graphql[type]}{${type}_${model}(objects:[${addFieldString}]){affected_rows}}`;
      }
      break;
    case 'update':
      const updateFields = fields as IUpdateFields;
      let updateFieldsString = getAddFieldString(updateFields);

      query = `${graphql[type]}($where:${model}WhereType){update_${model}(_set:{${updateFieldsString}},where:$where){affected_rows}} `;
      const editWhere = where as IWhere[];
      variables = {
        where: getGraphQLWhere(editWhere),
      }
      break;

    case 'del':
      const delFields = fields as IAddFields;
      const deletWhere = where as IWhere[];
      variables = {
        where: getGraphQLWhere(deletWhere),
      }
      if (delFields.type == 'one') {
        query = `${graphql[type]}{delete_${model}_by_id(id:${deletWhere[0].value},isPhysical:${delMethod})} `
      } else if (delFields.type == 'more') {
        query = `${graphql[type]}($where:${model}WhereType){delete_${model}(where:$where,isPhysical:${delMethod}){affected_rows}} `
      }
      ; break;

    case 'query':
      const queryFields = fields as IFields;
      const fieldStrings = queryFields?.map((item) => {
        const typeIndex = item.format?.FormatType;
        const formatMap = typeIndex ? `@${graphQLFormatMap[typeIndex]}(${graphParamMap[typeIndex](item?.format)})` : '';
        return `${item.field.name} ${formatMap}`;
      });
      let fieldString = fieldStrings?.join(',');
      query = `${graphql[type]}($where:${model}WhereType,$order:${model}Order,$size:Int,$index:Int) {${model}(where:$where,order:$order,size:$size,index:$index){${fieldString ?? ''}}}`;

      const searchWhere = where as IWhere[];
      variables = {
        where: getGraphQLWhere(searchWhere),
        order: order ?? {},
        index: index ?? 1,
        size: size ?? 20
      }
      break;
  }


  let graphQL = {
    query: query,
    variables: variables
  };
  return graphQL
}

/** 获取GraphQLq接口request所需参数
 * **/
export const getRequestGraphQL = (props: IGraphql) => {
  const graphQL = getGraphQL(props);
  return {
    query: graphQL.query,
    variables: graphQL.variables
  }
}



interface ITypeValue {
  type: IValueSource;
  typeValue: any;//根据类型选择的值
  inputValue: any;//元件的输入值
  inbiz: any;
  context: any
}


/** 根据类型和值获取真实的值
 * **/
export const getValueByType = (props: ITypeValue) => {
  const { type, typeValue, inputValue, inbiz, context } = props;
  let value = ''
  switch (type) {
    case 'const': value = typeValue; break;
    case 'comp': value = inbiz(typeValue)?.value; break;
    case 'context': value = context?.[typeValue]; break;
    case 'node': value = inputValue?.[typeValue]; break;
  }

  return value
}

